package com.zxtech.zzm.ftp

import org.apache.commons.net.ftp.FTP
import org.apache.commons.net.ftp.FTPClient
import org.apache.commons.net.ftp.FTPClientConfig
import org.apache.commons.net.ftp.FTPReply
import java.io.*
import java.nio.charset.Charset
import java.util.*

class FTP {
    /**
     * 服务器名.
     */
    private val hostName: String

    /**
     * 端口号
     */
    private val serverPort: Int

    /**
     * 用户名.
     */
    private val userName: String

    /**
     * 密码.
     */
    private val password: String

    /**
     * FTP连接.
     */
    private val ftpClient: FTPClient?
    // -------------------------------------------------------文件上传方法------------------------------------------------
    /**
     * 上传单个文件.
     *
     * @param remotePath FTP目录
     * @param listener 监听器
     * @throws IOException
     */
    @Throws(IOException::class)
    fun uploadSingleFile(singleFile: File, remotePath: String, listener: UploadProgressListener) {
        // 上传之前初始化
        uploadBeforeOperate(remotePath, listener)
        val flag: Boolean
        flag = uploadingSingle(singleFile, listener)
        if (flag) {
            listener.onUploadProgress(FTP_UPLOAD_SUCCESS, 0, singleFile)
        } else {
            listener.onUploadProgress(FTP_UPLOAD_FAIL, 0, singleFile)
        }
        // 上传完成之后关闭连接
        uploadAfterOperate(listener)
    }

    /**
     * 上传多个文件.
     *
     * @param remotePath FTP目录
     * @param listener 监听器
     * @throws IOException
     */
    @Throws(IOException::class)
    fun uploadMultiFile(
        fileList: LinkedList<File>, remotePath: String,
        listener: UploadProgressListener
    ) {
        // 上传之前初始化
        uploadBeforeOperate(remotePath, listener)
        var flag: Boolean
        for (singleFile in fileList) {
            flag = uploadingSingle(singleFile, listener)
            if (flag) {
                listener.onUploadProgress(FTP_UPLOAD_SUCCESS, 0, singleFile)
            } else {
                listener.onUploadProgress(FTP_UPLOAD_FAIL, 0, singleFile)
            }
        }
        // 上传完成之后关闭连接
        uploadAfterOperate(listener)
    }

    /**
     * 上传单个文件.
     *
     * @param localFile 本地文件
     * @return true上传成功, false上传失败
     * @throws IOException
     */
    @Throws(IOException::class)
    private fun uploadingSingle(localFile: File, listener: UploadProgressListener): Boolean {
        var flag = true
        // 不带进度的方式
        // // 创建输入流
        // InputStream inputStream = new FileInputStream(localFile);
        // // 上传单个文件
        // flag = ftpClient.storeFile(localFile.getName(), inputStream);
        // // 关闭文件流
        // inputStream.close();
        // 带有进度的方式
        val buffIn = BufferedInputStream(FileInputStream(localFile))
        val progressInput = ProgressInputStream(buffIn, listener, localFile)
        flag = ftpClient!!.storeFile(
            String(
                localFile.name.toByteArray(charset("UTF-8")),
                Charset.forName("ISO-8859-1")
            ), progressInput
        )
        buffIn.close()
        return flag
    }

    /**
     * 上传文件之前初始化相关参数
     *
     * @param remotePath FTP目录
     * @param listener 监听器
     * @throws IOException
     */
    @Throws(IOException::class)
    private fun uploadBeforeOperate(remotePath: String, listener: UploadProgressListener) {
        // 打开FTP服务
        try {
            openConnect()
            listener.onUploadProgress(FTP_CONNECT_SUCCESSS, 0, null)
        } catch (e1: IOException) {
            e1.printStackTrace()
            listener.onUploadProgress(FTP_CONNECT_FAIL, 0, null)
            return
        }
        // 设置模式
        ftpClient!!.enterLocalPassiveMode()
        ftpClient.setFileTransferMode(FTP.STREAM_TRANSFER_MODE)
        // FTP下创建文件夹
        ftpClient.makeDirectory(
            String(remotePath.toByteArray(charset("UTF-8")), Charset.forName("ISO-8859-1"))
        )
        // 改变FTP目录
        ftpClient.changeWorkingDirectory(remotePath)
        // 上传单个文件
    }

    /**
     * 上传完成之后关闭连接
     *
     * @throws IOException
     */
    @Throws(IOException::class)
    private fun uploadAfterOperate(listener: UploadProgressListener) {
        closeConnect()
        listener.onUploadProgress(FTP_DISCONNECT_SUCCESS, 0, null)
    }
    // -------------------------------------------------------文件下载方法------------------------------------------------
    /**
     * 下载单个文件，可实现断点下载.
     *
     * @param serverPath Ftp目录及文件路径
     * @param localPath 本地目录
     * @param fileName 下载之后的文件名称
     * @param listener 监听器
     * @throws IOException
     */
    @Throws(Exception::class)
    fun downloadSingleFile(
        serverPath: String?, localPath: String, fileName: String,
        listener: DownLoadProgressListener
    ) {
        // 打开FTP服务
        var localPath = localPath
        try {
            openConnect()
            listener.onDownLoadProgress(FTP_CONNECT_SUCCESSS, 0, null)
        } catch (e1: IOException) {
            e1.printStackTrace()
            listener.onDownLoadProgress(FTP_CONNECT_FAIL, 0, null)
            return
        }
        // 先判断服务器文件是否存在
        val files = ftpClient!!.listFiles(serverPath)
        if (files.size == 0) {
            listener.onDownLoadProgress(FTP_FILE_NOTEXISTS, 0, null)
            return
        }
        //创建本地文件夹
        val mkFile = File(localPath)
        if (!mkFile.exists()) {
            mkFile.mkdirs()
        }
        localPath = localPath + fileName
        // 接着判断下载的文件是否能断点下载
        val serverSize = files[0].size // 获取远程文件的长度
        val localFile = File(localPath)
        var localSize: Long = 0
        if (localFile.exists()) {
            localSize = localFile.length() // 如果本地文件存在，获取本地文件的长度
            if (localSize >= serverSize) {
                val file = File(localPath)
                file.delete()
            }
        }
        // 进度
        val step = serverSize / 100
        var process: Long = 0
        var currentSize: Long = 0
        // 开始准备下载文件
        val out: OutputStream = FileOutputStream(localFile, true)
        ftpClient.restartOffset = localSize
        val input = ftpClient.retrieveFileStream(serverPath)
        val b = ByteArray(1024)
        var length = 0
        while (input.read(b).also { length = it } != -1) {
            out.write(b, 0, length)
            currentSize = currentSize + length
            if (currentSize / step != process) {
                process = currentSize / step
                if (process % 5 == 0L) {  //每隔%5的进度返回一次
                    listener.onDownLoadProgress(FTP_DOWN_LOADING, process, null)
                }
            }
        }
        out.flush()
        out.close()
        input.close()
        // 此方法是来确保流处理完毕，如果没有此方法，可能会造成现程序死掉
        if (ftpClient.completePendingCommand()) {
            listener.onDownLoadProgress(FTP_DOWN_SUCCESS, 0, File(localPath))
        } else {
            listener.onDownLoadProgress(FTP_DOWN_FAIL, 0, null)
        }
        // 下载完成之后关闭连接
        closeConnect()
        listener.onDownLoadProgress(FTP_DISCONNECT_SUCCESS, 0, null)
        return
    }

    //下载文件的重载方法
    @Throws(Exception::class)
    fun downloadSingleFile(
        serverPath: String?, localPath: String, fileName: String,
        listener: DownLoadDetailProgressListener
    ) {
        // 打开FTP服务
        var localPath = localPath
        try {
            openConnect()
            listener.onDownLoadProgress(FTP_CONNECT_SUCCESSS, 0, 0, 0, null)
        } catch (e1: IOException) {
            e1.printStackTrace()
            listener.onDownLoadProgress(FTP_CONNECT_FAIL, 0, 0, 0, null)
            return
        }
        // 先判断服务器文件是否存在
        val files = ftpClient!!.listFiles(serverPath)
        if (files.size == 0) {
            listener.onDownLoadProgress(FTP_FILE_NOTEXISTS, 0, 0, 0, null)
            return
        }
        //创建本地文件夹
        val mkFile = File(localPath)
        if (!mkFile.exists()) {
            mkFile.mkdirs()
        }
        localPath = localPath + fileName
        // 接着判断下载的文件是否能断点下载
        val serverSize = files[0].size // 获取远程文件的长度
        val localFile = File(localPath)
        //************  backup code origin here  ******
        //    long localSize = 0;
        //    if (localFile.exists()) {
        //      localSize = localFile.length(); // 如果本地文件存在，获取本地文件的长度
        //      if (localSize >= serverSize) {
        //        File file = new File(localPath);
        //        file.delete();
        //      }
        //    }
        //**********  ****************************  ****
        localFile.delete()
        // 进度
        val step = serverSize / 100
        var process: Long = 0
        var currentSize: Long = 0
        // 开始准备下载文件
        //************  backup code origin here  ******
        //    OutputStream out = new FileOutputStream(localFile, true);
        //    ftpClient.setRestartOffset(localSize);
        //**********  ****************************  ****
        val out: OutputStream = FileOutputStream(localFile, false)
        ftpClient.restartOffset = 0
        val input = ftpClient.retrieveFileStream(serverPath)
        val b = ByteArray(1024)
        var length = 0
        while (input.read(b).also { length = it } != -1) {
            out.write(b, 0, length)
            currentSize = currentSize + length
            if (currentSize / step != process) {
                process = currentSize / step
                if (process % 5 == 0L) {  //每隔%5的进度返回一次
                    listener.onDownLoadProgress(
                        FTP_DOWN_LOADING,
                        serverSize,
                        currentSize,
                        process,
                        null
                    )
                }
            }
        }
        out.flush()
        out.close()
        input.close()
        // 此方法是来确保流处理完毕，如果没有此方法，可能会造成现程序死掉
        if (ftpClient.completePendingCommand()) {
            listener.onDownLoadProgress(FTP_DOWN_SUCCESS, 0, 0, 0, File(localPath))
        } else {
            listener.onDownLoadProgress(FTP_DOWN_FAIL, 0, 0, 0, null)
        }
        // 下载完成之后关闭连接
        closeConnect()
        listener.onDownLoadProgress(FTP_DISCONNECT_SUCCESS, 0, 0, 0, null)
        return
    }
    // -------------------------------------------------------文件删除方法------------------------------------------------
    /**
     * 删除Ftp下的文件.
     *
     * @param serverPath Ftp目录及文件路径
     * @param listener 监听器
     * @throws IOException
     */
    @Throws(Exception::class)
    fun deleteSingleFile(serverPath: String?, listener: DeleteFileProgressListener) {
        // 打开FTP服务
        try {
            openConnect()
            listener.onDeleteProgress(FTP_CONNECT_SUCCESSS)
        } catch (e1: IOException) {
            e1.printStackTrace()
            listener.onDeleteProgress(FTP_CONNECT_FAIL)
            return
        }
        // 先判断服务器文件是否存在
        val files = ftpClient!!.listFiles(serverPath)
        if (files.size == 0) {
            listener.onDeleteProgress(FTP_FILE_NOTEXISTS)
            return
        }
        //进行删除操作
        var flag = true
        flag = ftpClient.deleteFile(serverPath)
        if (flag) {
            listener.onDeleteProgress(FTP_DELETEFILE_SUCCESS)
        } else {
            listener.onDeleteProgress(FTP_DELETEFILE_FAIL)
        }
        // 删除完成之后关闭连接
        closeConnect()
        listener.onDeleteProgress(FTP_DISCONNECT_SUCCESS)
        return
    }
    // -------------------------------------------------------打开关闭连接------------------------------------------------
    /**
     * 打开FTP服务.
     *
     * @throws IOException
     */
    @Throws(IOException::class)
    fun openConnect() {
        // 中文转码
        ftpClient!!.controlEncoding = "UTF-8"
        var reply: Int // 服务器响应值
        // 连接至服务器
        ftpClient.connect(hostName, serverPort)
        // 获取响应值
        reply = ftpClient.replyCode
        if (!FTPReply.isPositiveCompletion(reply)) {
            // 断开连接
            ftpClient.disconnect()
            throw IOException("connect fail: $reply")
        }
        // 登录到服务器
        ftpClient.login(userName, password)
        // 获取响应值
        reply = ftpClient.replyCode
        if (!FTPReply.isPositiveCompletion(reply)) {
            // 断开连接
            ftpClient.disconnect()
            throw IOException("connect fail: $reply")
        } else {
            // 获取登录信息
            val config = FTPClientConfig(ftpClient.systemType.split(" ").toTypedArray()[0])
            config.serverLanguageCode = "zh"
            ftpClient.configure(config)
            // 使用被动模式设为默认
            ftpClient.enterLocalPassiveMode()
            // 二进制文件支持
            ftpClient.setFileType(FTP.BINARY_FILE_TYPE)
        }
    }

    /**
     * 关闭FTP服务.
     *
     * @throws IOException
     */
    @Throws(IOException::class)
    fun closeConnect() {
        if (ftpClient != null) {
            // 退出FTP
            ftpClient.logout()
            // 断开连接
            ftpClient.disconnect()
        }
    }

    // ---------------------------------------------------上传、下载、删除监听---------------------------------------------
    /*
   * 上传进度监听
   */
    interface UploadProgressListener {
        fun onUploadProgress(currentStep: String?, uploadSize: Long, file: File?)
    }

    /*
   * 下载进度监听
   */
    interface DownLoadProgressListener {
        fun onDownLoadProgress(currentStep: String?, downProcess: Long, file: File?)
    }

    interface DownLoadDetailProgressListener {
        fun onDownLoadProgress(
            currentStep: String?, fileSize: Long, downloadedSize: Long,
            downProcess: Long, file: File?
        )
    }

    /*
   * 文件删除监听
   */
    interface DeleteFileProgressListener {
        fun onDeleteProgress(currentStep: String?)
    }

    companion object {
        const val FTP_CONNECT_SUCCESSS = "ftp连接成功"
        const val FTP_CONNECT_FAIL = "ftp连接失败"
        const val FTP_DISCONNECT_SUCCESS = "ftp断开连接"
        const val FTP_FILE_NOTEXISTS = "ftp上文件不存在"
        const val FTP_UPLOAD_SUCCESS = "ftp文件上传成功"
        const val FTP_UPLOAD_FAIL = "ftp文件上传失败"
        const val FTP_UPLOAD_LOADING = "ftp文件正在上传"
        const val FTP_DOWN_LOADING = "ftp文件正在下载"
        const val FTP_DOWN_SUCCESS = "ftp文件下载成功"
        const val FTP_DOWN_FAIL = "ftp文件下载失败"
        const val FTP_DELETEFILE_SUCCESS = "ftp文件删除成功"
        const val FTP_DELETEFILE_FAIL = "ftp文件删除失败"
    }

    init {
        hostName = Config.FTP_SERVER_IP
        serverPort = Config.FTP_SERVER_PORT
        userName = Config.FTP_USER_NAME
        password = Config.FTP_PASSWROD
        ftpClient = FTPClient()
    }
}